#include "StdAfx.h"


GraphicsManager::GraphicsManager(void)
{
	Init();
}
GraphicsManager::~GraphicsManager(void)
{
	cgDestroyContext(cgContext);
}
void GraphicsManager::Init()
{
	nFramesElapsed=0;
	CurLightMapID=0;
	DebugAngle=0;
	bAutoShading=true;
	bClearSpecular=true;
	bUseLightPass=true;
	bDebugDraw=false;
	bDoubleShadows=false;
	ClearColor=DefaultClearColor=Vec3(0,0,0);
	NearViewPlane=1;
	FarViewPlane=2000;
	FloorTiles=20;
	BloomFactor=.8;
	LightCutoff=0.7;
	//initialize various modules
	InitCg();
	InitIL();
}
void GraphicsManager::Resize(int width, int height)
{
	WinWidth=width;
	WinHeight=height;
}
void GraphicsManager::InitMaps()
{
	//init color maps
	TextureDesc tdesc;
	tdesc.InternalFormat=GL_RGBA16;
	for(int i=0;i<2;i++)
	{
		TotalLightMap[i]=sptr<GameResource>(new Texture(tdesc));
		TotalSpecularMap[i]=sptr<GameResource>(new Texture(tdesc));
		BlurredMap[i]=sptr<GameResource>(new Texture(tdesc));
	}
	DiffuseMap=sptr<GameResource>(new Texture(tdesc));
	DeferredMap=sptr<GameResource>(new Texture(tdesc));
	BloomedMap=sptr<GameResource>(new Texture(tdesc));
	LightPassMap=sptr<GameResource>(new Texture(tdesc));
	for(int i=0;i<4;i++)
	{
		tdesc.Width*=.5f;
		tdesc.Height*=.5f;
		DownsampleMap[i]=sptr<GameResource>(new Texture(tdesc));
	}
	//init env maps
	tdesc.Width=tdesc.Height=TEXTURE_RESOLUTION;
	tdesc.Target=GL_TEXTURE_CUBE_MAP;
	tdesc.bGenerateMipmaps=true;
	EnvMap=sptr<GameResource>(new Texture(tdesc));
	//init depth maps
	tdesc.Target=GL_TEXTURE_2D;
	tdesc.Width=TEXTURE_RESOLUTION;
	tdesc.Height=TEXTURE_RESOLUTION;
	tdesc.Format=GL_DEPTH_COMPONENT;
	tdesc.InternalFormat=GL_DEPTH_COMPONENT32;
	tdesc.bGenerateMipmaps=true;
	tdesc.bDepthCompare=true;
	tdesc.Type=GL_FLOAT;
	ShadowMap=sptr<GameResource>(new Texture(tdesc));
	tdesc.bDepthCompare=false;
	DepthMap=sptr<GameResource>(new Texture(tdesc));

	DisplayMap=BloomedMap;
}
void GraphicsManager::InitGL(HDC hdc)
{
	static PIXELFORMATDESCRIPTOR pfd =
	{
		sizeof(PIXELFORMATDESCRIPTOR),
		1,
		PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
		PFD_TYPE_RGBA,
		32, // bit depth
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		16, // z-buffer depth
		0, 0, 0, 0, 0, 0, 0,
	};
	this->hdc=hdc;
	// Pixel format.
	nPixelFormat = ChoosePixelFormat(hdc, &pfd);
	SetPixelFormat(hdc, nPixelFormat, &pfd);

	// Create the OpenGL Rendering Context.
	hrc = wglCreateContext(hdc);
	wglMakeCurrent(hdc, hrc);
	// Set color to use when clearing the background.
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	glClearDepth(1.0f);
	InitGLState();
	glewInit();
	FrameBufferDesc fbdesc;
	FB=sptr<FrameBuffer>(new FrameBuffer(fbdesc));
	InitMaps();
	glMatrixMode(GL_PROJECTION);
		glPushMatrix();
	glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
}
void GraphicsManager::InitGLState()
{
	_err("Error occured before init state");
	// Turn on backface culling
	glFrontFace(GL_CCW);
	glCullFace(GL_BACK);
	//glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
	//glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
	// Turn on depth testing
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	_err("Error occured before color enable");
	//glEnable(GL_COLOR);
	_err("Error occured after init state");
	
}
void GraphicsManager::InitCg()
{
	//create the context
	cgContext=cgCreateContext();
	ReportError_CG("Failed to initialize CG context");
	//init cgFX
	cgGLRegisterStates(cgContext);
	ReportError_CG("Failed to register states!");
	//automate texture management
	cgGLSetManageTextureParameters(cgContext, CG_TRUE);
}
sptr<GameResource> GraphicsManager::GetBestShader(sptr<ModelComponent> mc)
{
	sptr<GameResource> shader_resc;
	Model* model=_MODEL(mc->Model);
	Material* material=_MATERIAL(model->Material);
	bool bNormal=material->NormalMap.get()!=NULL;
	if(mc->bReflects)
	{
		if(bNormal)
			shader_resc=_RESC("BumpReflection_Shader");
		else shader_resc=_RESC("Reflection_Shader");
	}
	else if(mc->bRefracts)
	{
		if(bNormal)
			shader_resc=_RESC("BumpRefraction_Shader");
		else shader_resc=_RESC("Refraction_Shader");
	}
	else shader_resc=_RESC("Basic_Shader");
	return shader_resc;
}
Vec2 GraphicsManager::GetDimensions()
{
	return Vec2(Width, Height);
}
CGcontext GraphicsManager::GetContext()
{
	return cgContext;
}
void GraphicsManager::InitIL()
{
	ilInit();
	iluInit();
	ilutRenderer(ILUT_OPENGL);
}
void GraphicsManager::UpdateViewProj()
{
	globals.Game.MainScene->MainCamera->UpdateView(Width, Height);
}
void GraphicsManager::ResetDimensions()
{
	Width=WinWidth;
	Height=WinHeight;
}
void GraphicsManager::ClearView()
{
	glClearColor(ClearColor.x, ClearColor.y, ClearColor.z, 1);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
void GraphicsManager::DrawDeferred()
{
	bAutoShading=true;
	bDrawSkybox=true;
	FB->Bind();
	FB->AttachToColor(DiffuseMap, 0);
	FB->AttachToDepth(DepthMap);
	FB->EnableColors(1);
	ClearColor=DefaultClearColor;
	ClearView();
	DrawScene();
	FB->Unbind();
}
void GraphicsManager::LightDeferred()
{
	sptr<GameResource> shader_resc=_RESC("DeferredLightingBlend_Shader");
	Shader* shader=_SHADER(shader_resc);
	shader->LoadTexture("DiffuseSampler", DiffuseMap);
	shader->LoadTexture("DiffuseLightingSampler", TotalLightMap[GetCurLightMapID()]);
	shader->LoadTexture("SpecularLightingSampler", TotalSpecularMap[GetCurLightMapID()]);
	FB->Bind();
	FB->AttachToColor(DeferredMap,0);
	FB->EnableColors(1);
	shader->BeginPass("p0");
		DrawFullScreenQuad();
	shader->EndPass();
	FB->Unbind();
}
void GraphicsManager::ClearMapToColor(sptr<GameResource> map_resc, Vec3 color)
{
	FB->Bind();
	ClearColor=color;
	FB->AttachToColor(map_resc,0);
	FB->EnableColors(1);
	ClearView();
	FB->Unbind();
}
void GraphicsManager::ClearDepth(sptr<GameResource> map_resc)
{
	FB->Bind();
	FB->AttachToDepth(map_resc);
	FB->DisableColors();
	Vec3 tempColor=ClearColor;
	ClearColor=Vec3(1,1,1);
	ClearView();
	ClearColor=tempColor;
	FB->Unbind();
}
void GraphicsManager::BuildLightMap()
{
	bAutoShading=false;	
	bDrawSkybox=false;
	//init default maps
	//clear one light map to ambient light
	ClearMapToColor(TotalLightMap[GetCurLightMapID()], globals.Game.MainScene->AmbientLight);
	//clear one specular to black
	if(bClearSpecular)
		ClearMapToColor(TotalSpecularMap[GetCurLightMapID()], Vec3(0,0,0));
	//get all lights in the scene
	BBVector<sptr<Actor>> lights=_ALL_LIGHTS;
	//for every light in the scene
	for(unsigned int i=0;i<lights.size();i++)
	{
		//swap the light maps
		RotateLightMap();
		//generate a shadow map
		BuildShadowMap(lights[i]);
		//build the next iteration of the light map
		MainShaderResc=_RESC("BumpLightGeneration_Shader");
		Shader* shader=_SHADER(MainShaderResc);
		//load light specific textures
		shader->LoadTexture("ShadowSampler",		ShadowMap);
		shader->LoadTexture("CurLightSampler",		TotalLightMap[GetOtherLightMapID()]);
		shader->LoadTexture("CurSpecularSampler",	TotalSpecularMap[GetOtherLightMapID()]);
		shader->LightActor=lights[i];
		shader->MainCamera=_MAIN_CAMERA;
		//clear the color maps
		ClearMapToColor(TotalLightMap[GetCurLightMapID()], Vec3(1,1,1));
		ClearMapToColor(TotalSpecularMap[GetCurLightMapID()], Vec3(0,0,0));
		ClearDepth(DepthMap);
		//attach the colors
		FB->Bind();
		FB->AttachToColor(TotalLightMap[GetCurLightMapID()],0);
		FB->AttachToColor(TotalSpecularMap[GetCurLightMapID()],1);
		FB->AttachToDepth(DepthMap);
		FB->EnableColors(2);
		DrawScene();
		FB->Unbind();
		//add light and specular maps together
		AdditiveBlend(TotalLightMap[GetCurLightMapID()], TotalLightMap[GetCurLightMapID()], TotalLightMap[GetOtherLightMapID()]);
		AdditiveBlend(TotalSpecularMap[GetCurLightMapID()], TotalSpecularMap[GetCurLightMapID()], TotalSpecularMap[GetOtherLightMapID()]);
	}
	ClearColor=DefaultClearColor;
}
void GraphicsManager::RotateLightMap()
{
	CurLightMapID=GetOtherLightMapID();
}
unsigned int GraphicsManager::GetCurLightMapID()
{
	return CurLightMapID;
}
void GraphicsManager::DrawTextureToTarget(sptr<GameResource> src, sptr<GameResource> dest, sptr<GameResource> shader_resc)
{
	Shader* shader=_SHADER(shader_resc);
	Texture* texture=_TEXTURE(dest);
	FB->Bind();
	glDisable(GL_DEPTH_TEST);
	glDepthMask(GL_FALSE);
	FB->Reset();
	FB->EnableColors(1);
	FB->AttachToColor(dest);
	shader->BeginPass("p0");
		DrawFullScreenTexture(src);
	shader->EndPass();
	glDepthMask(GL_TRUE);
	glEnable(GL_DEPTH_TEST);
	FB->Unbind();
}
void GraphicsManager::Downsample()
{
	sptr<GameResource> shader_resc=_RESC("Downsample_Shader");
	sptr<GameResource> texture_resc;
	if(bUseLightPass)
		texture_resc=LightPassMap;
	else texture_resc=DeferredMap;

	Shader* shader=_SHADER(shader_resc);
	float tex=2.0f/(float)TEXTURE_RESOLUTION;
	for(int i=0;i<4;i++)
	{
		float diff=1.0f;
		shader->SetVec2("texelSize", Vec2(tex*diff, tex*diff));
		DrawTextureToTarget(texture_resc, DownsampleMap[i], shader_resc);
		texture_resc=DownsampleMap[i];
		diff*=.5f;
	}
}
void GraphicsManager::Blur()
{
	sptr<GameResource> shader_resc=_RESC("GaussianBlur_Shader");
	Shader* shader=_SHADER(shader_resc);
	Vec2 dir=Vec2(4.0f/(float)TEXTURE_RESOLUTION, 0);
	shader->SetVec2("dir", dir);
	DrawTextureToTarget(DownsampleMap[3], BlurredMap[0], shader_resc);
	dir=Vec2(0, 4.0f/(float)TEXTURE_RESOLUTION);
	shader->SetVec2("dir", dir);
	DrawTextureToTarget(BlurredMap[0], BlurredMap[1], shader_resc);
}
void GraphicsManager::Bloom()
{
	Vec2 factors=Vec2(1,BloomFactor);
	AdditiveBlend(BloomedMap, DeferredMap, BlurredMap[1], factors);
}
void GraphicsManager::LightPass()
{
	sptr<GameResource> shader_resc=_RESC("LightPass_Shader");
	Shader* shader=_SHADER(shader_resc);
	shader->SetReal("lightCutoff", LightCutoff);
	DrawTextureToTarget(DeferredMap, LightPassMap, shader_resc);
}
void GraphicsManager::ApplyBloom()
{
	if(bUseLightPass)
		LightPass();
	Downsample();
	Blur();
	Bloom();
}
unsigned int GraphicsManager::GetOtherLightMapID()
{
	return (CurLightMapID+1)%2;
}
void GraphicsManager::Render()
{
	ResetDimensions();
	ClearView();
	if(bEnableShaders)
	{
		UpdateViewProj();
		if(_PLAYER->Models[0]->bReflects
			||_PLAYER->Models[0]->bRefracts)
			BuildEnvMap(_PLAYER->Pos);
		DrawDeferred();
		BuildLightMap();
		LightDeferred();
		ApplyBloom();
		glViewport(0,0,Width,Height);
		DrawFullScreenTexture(DisplayMap);
		Shader* shader=_SHADER(_RESC("Skybox_Shader"));
		shader->BeginPass("p0");
		DrawHUD();
		shader->EndPass();
	}
	else 
	{
		UpdateViewProj();
		DrawScene();
	}
	nFramesElapsed++;
	_err("Uncaught errors: ");
}
void GraphicsManager::DrawHUD()
{
	glRasterPos2f(100, 100);
	glColor4f(1,1,1,1);
	glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, 'A');
}
void GraphicsManager::DrawFullScreenTexture(sptr<GameResource> tex_resc)
{
	Texture* texture=_TEXTURE(tex_resc);
	if(texture)
	{
		ClearView();
		UpdateViewProj();
		_MAIN_CAMERA->ResetView();
		texture->Bind();
		DrawFullScreenQuad();
		texture->Unbind();
	}
	else _log("Attempting to draw null texture.");
}
void GraphicsManager::DrawSkybox()
{
	sptr<GameResource> shader_resc=_RESC("Skybox_Shader"), texture_resc=_RESC("Sky_Texture"), skybox_resc=_RESC("Sphere");
	if(!bDrawSkybox)
		return;
	Shader* shader=_SHADER(shader_resc);
	Mesh* mesh=_MESH(skybox_resc);
	if(shader&&texture_resc&&mesh)
	{
		shader->LoadTexture("DiffuseSampler", texture_resc);
		Vec3 tempPos;
		if(!_MAIN_CAMERA->bFocus)
		{
			tempPos=globals.Game.MainScene->MainCamera->Pos;
			globals.Game.MainScene->MainCamera->Move(-tempPos);
		}

		shader->MainCamera=_MAIN_CAMERA;
		shader->Translation=_MAIN_CAMERA->GetPos();
		shader->Rotation=(Quat)Rotator(0,0,90);
		float radius=mesh->GetRadius();
		shader->Scale=Vec3(globals.Graphics.FarViewPlane,globals.Graphics.FarViewPlane,globals.Graphics.FarViewPlane)/10;
		shader->BeginPass("p0");
			mesh->Draw();
		shader->EndPass();
		if(!_MAIN_CAMERA->bFocus)	
			globals.Game.MainScene->MainCamera->Move(tempPos);
	}
}
void GraphicsManager::BuildShadowMap(sptr<Actor> LightActor)
{
	MainShaderResc=_RESC("ShadowGeneration_Shader");
	bAutoShading=false;
	Light* light=_LIGHT(LightActor);
	if(light)
	{
		ClearDepth(ShadowMap);
		sptr<Camera> tempCamera=_MAIN_CAMERA;
		_MAIN_CAMERA=light->GetCamera();
		FB->Bind();
		FB->AttachToDepth(ShadowMap);
		FB->DisableColors();
		DrawScene();
		FB->Unbind();
		_MAIN_CAMERA=tempCamera;
		Texture* texture=_TEXTURE(ShadowMap);
		texture->GenerateMipMaps();
	}
}
void GraphicsManager::AdditiveBlend(sptr<GameResource> dest_map, sptr<GameResource> map1, sptr<GameResource> map2, Vec2 factors)
{
	sptr<GameResource> shader_resc=_RESC("AddBlend_Shader");
	Shader* shader=_SHADER(shader_resc);
	if(shader)
	{
		shader->LoadTexture("Sampler1",map1);
		shader->LoadTexture("Sampler2",map2);
		shader->SetVec2("factors", factors);
		FB->Bind();
		FB->AttachToColor(dest_map,0);
		FB->EnableColors(1);
		shader->BeginPass("p0");
			DrawFullScreenQuad();
		shader->EndPass();
		FB->Unbind();
	}
}
void GraphicsManager::DrawFloor()
{
	sptr<GameResource> shader_resc;
	if(bAutoShading)
		shader_resc=_RESC("Basic_Shader");
	else shader_resc=MainShaderResc;
	Shader* shader=_SHADER(shader_resc);
	shader->MainCamera=_MAIN_CAMERA;
	shader->LoadMaterial(_RESC("BrickMat"));
	shader->Rotation=(Quat)Rotator();
	shader->Translation=Vec3(0,0,0);
	shader->Scale=Vec3(1,1,1);
	shader->BeginPass("p0");
		DrawFloorBox(FloorWidth, 30, FloorTiles, 1);
	shader->EndPass();
}
void GraphicsManager::DrawScene()
{
	UpdateViewProj();
	DrawSkybox();
	DrawFloor();
	for(unsigned int i=0;i<globals.Game.MainScene->Actors.size();i++)
	{
		sptr<Actor> actor=globals.Game.MainScene->Actors[i];
		DrawPawn(actor);
	}
}
//get the environment map
void GraphicsManager::BuildEnvMap(Vec3 pos)
{
	bAutoShading=false;
	bDrawSkybox=true;
	MainShaderResc=_RESC("Basic_Shader");
	//create a new camera and backup the old one
	sptr<Camera> tempCamera=_MAIN_CAMERA;
	CameraDesc cdesc;
	cdesc.FOV=90;
	cdesc.Pos=pos;
	sptr<Camera> camera=sptr<Camera>(new Camera(cdesc));
	_MAIN_CAMERA=camera;
	//init 6 cam states
	Rotator rot[6]={Rotator(-90),Rotator(90),
					Rotator(0,90), Rotator(0,-90),
					Rotator(180),Rotator()};
	//start rendering
	FB->Bind();
	FB->AttachToDepth(DepthMap);
	//for every face
	for(unsigned int i=0;i<6;i++)
	{		
		camera->ResetLook();
		//rotate the camera
		camera->Rotate(rot[i]);
		//set the up vector
		camera->SetUp(Vec3(0,-1,0));
		if(rot[i].pitch)
			camera->SetUp(Vec3(0,0,-1));
		//render scene
		FB->AttachToColor(EnvMap, 0, i);
		//ClearDepth(DepthMap);
		ClearView();
		DrawScene();
	}
	_TEXTURE(EnvMap)->bGenerateMipmaps=true;
	_TEXTURE(EnvMap)->GenerateMipMaps();
	//restore initial state
	FB->Reset();
	FB->Unbind();
	_MAIN_CAMERA=tempCamera;
	_err("Uncaught error during env mapping");
}
void GraphicsManager::DrawPawn(sptr<Actor> pawn_actor)
{
	Pawn* pawn=_PAWN(pawn_actor);
	if(pawn)
	{
		if(pawn->bVisible)
			for(unsigned int i=0;i<pawn->Models.size();i++)
				DrawModelComponent(pawn->Models[i]);

		if(pawn->cloth)
			DrawPawnCloth(pawn_actor);		
	}
}
void GraphicsManager::DrawPawnCloth(sptr<Actor> pawn_actor)
{
	Pawn* pawn=_PAWN(pawn_actor);
	sptr<Cloth> cloth=pawn->cloth;
	sptr<GameResource> shader_resc;
	if(bAutoShading)
		shader_resc=_RESC("Skybox_Shader");
	/*else if(MainShaderResc->Name==string("BumpLightGeneration_Shader"))
		shader_resc=_RESC("LightGeneration_Shader");*/
	else shader_resc=MainShaderResc;
	Shader* shader=_SHADER(shader_resc);
	if(shader)
	{
		shader->MainCamera=_MAIN_CAMERA;
		shader->LoadMaterial(pawn->ClothMaterialResc);
		shader->Translation=Vec3(0,0,0);
		shader->Rotation=Quat(Vec3(0,1,0), 0);
		shader->Scale=Vec3(-1,1,-1);
		shader->BeginPass("p0");
			cloth->draw();
		shader->EndPass();
	}
}
void GraphicsManager::DrawModelComponent(sptr<ModelComponent> mc)
{
	Pawn*		pawn=mc->Owner;
	Model*		model=_MODEL(mc->Model);
	Material*	material=_MATERIAL(model->Material);
	Mesh*		mesh=_MESH(model->Mesh);
	sptr<GameResource> shader_resc;
	if(bAutoShading)
		shader_resc=GetBestShader(mc);
	else shader_resc=MainShaderResc;
	Shader* shader=_SHADER(shader_resc);
	if(shader)
	{
		shader->LoadTexture("EnvSampler", EnvMap);
		shader->SetReal("EtaRatio", .2);
		shader->LoadMaterial(model->Material);
		shader->SetReal("diffuseFactor", mc->DiffuseFactor);
		Vec3 model_pos=mc->GetPos();
		pawn->quat.rotate(model_pos);
		Vec3 pos=pawn->GetPos();
		Quat quat=pawn->quat;
		shader->Translation=pos;
		shader->Rotation=quat;
		shader->Scale=mc->Scale;
		shader->MainCamera=_MAIN_CAMERA;
		if(mesh)
		{
			shader->BeginPass("p0");
				mesh->Draw();
			shader->EndPass();
			if(bAutoShading&&bDebugDraw)
			{
				shader_resc=_RESC("Wireframe_Shader");
				shader=_SHADER(shader_resc);
				shader->Translation=pos;
				shader->Rotation=quat;
				shader->Scale=mc->Scale;
				shader->MainCamera=_MAIN_CAMERA;
				shader->BeginPass("p0");
					mesh->DebugDraw();
				shader->EndPass();
			}
		}
	}
	else _log("Couldn't find a suitable shader for drawing. "
		"Pawn: "<<pawn->Name<<" Model: "<<model->Name);

}
